bitkeeper revision 1.1389.1.16 (4273458eyxEjHSppjkQyDsXHHxOY9Q)
authorkaf24@firebug.cl.cam.ac.uk[kaf24] <kaf24@firebug.cl.cam.ac.uk[kaf24]>
Sat, 30 Apr 2005 08:45:02 +0000 (08:45 +0000)
committerkaf24@firebug.cl.cam.ac.uk[kaf24] <kaf24@firebug.cl.cam.ac.uk[kaf24]>
Sat, 30 Apr 2005 08:45:02 +0000 (08:45 +0000)
coredump.patch
Extend libxc and gdb server to coredump a domain.
Signed-off-by: Kip Macy <kmacy@fsmware.com>
.rootkeys
tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/Makefile.in
tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c
tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c [new file with mode: 0644]
tools/libxc/Makefile
tools/libxc/xc.h
tools/libxc/xc_core.c [new file with mode: 0644]
tools/libxc/xc_ptrace.c
tools/libxc/xc_ptrace_core.c [new file with mode: 0644]
tools/python/xen/lowlevel/xc/xc.c

index cd12228470ef3062f4810bc05f94d1524b84aaa8..e1444d613ae679a9e721038d8c47475ee476278b 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 423d3a7b2vJq86I8FbYm6up5BsCwfA tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.in
 423d3a7bQPownmVb63qOoyq89ebBVA tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/configure.srv
 423d3a7bHtqhyOgiRWhjWt-S-6wbYg tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/linux-xen-low.c
+4273458dYPghQKVnj_xu5-fC38CcOg tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c
 423d3a7b2ENk2IskDZYZ98pe5NsvIA tools/gdb/gdb-6.2.1-xen-sparse/mkbuildtree
 423d3a7buANO_q-kgxIRffUu7lMnUw tools/gdb/gdbbuild
 41e2ff6dNPgvIrdIF6dC1azdex1U3A tools/ioemu/Makefile
 3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/libxc/rpm.spec
 3fbba6dcrNxtygEcgJYAJJ1gCQqfsA tools/libxc/xc.h
 3fbba6dbEVkVMX0JuDFzap9jeaucGA tools/libxc/xc_bvtsched.c
+4273458dyF2_sKA6CFkNJQYb8eY2dA tools/libxc/xc_core.c
 3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/libxc/xc_domain.c
 40278d99BLsfUv3qxv0I8C1sClZ0ow tools/libxc/xc_elf.h
 403e0977Bjsm_e82pwvl9VvaJxh8Gg tools/libxc/xc_evtchn.c
 3fbba6dctWRWlFJkYb6hdix2X4WMuw tools/libxc/xc_private.c
 3fbba6dcbVrG2hPzEzwdeV_UC8kydQ tools/libxc/xc_private.h
 42337174PxyzzPk62raDiYCIsfStDg tools/libxc/xc_ptrace.c
+4273458duzL--nsTfT6e_q6Kfij48g tools/libxc/xc_ptrace_core.c
 41dde8b0pLfAKMs_L9Uri2hnzHiCRQ tools/libxc/xc_vmx_build.c
 40e1b09dMYB4ItGCqcMIzirdMd9I-w tools/libxutil/Makefile
 40e033325Sjqs-_4TuzeUEprP_gYFg tools/libxutil/allocate.c
index b6605f6698e895f836b19593d46dbdbb1e5849b3..93f177387cd12bdfe02d98febb3143bc5b103ccc 100644 (file)
@@ -83,7 +83,7 @@ READLINE_DEP = $$(READLINE_DIR)
 # -I. for config files.
 # -I${srcdir} for our headers.
 # -I$(srcdir)/../regformats for regdef.h.
-INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../regformats -I$(INCLUDE_DIR)
+INCLUDE_CFLAGS = -I. -I${srcdir} -I$(srcdir)/../regformats -I$(INCLUDE_DIR)  -I../../../../libxc/ -I../../../../libxutil/
 
 # M{H,T}_CFLAGS, if defined, has host- and target-dependent CFLAGS
 # from the config/ directory.
index c5cff23928a0ed42163b7da0d6b0ce4c0b70c107..0e35f30294af7cff1dc01583628299dbe1ea14a3 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
-
+#include <xc.h>
 #define TRACE_ENTER /* printf("enter %s\n", __FUNCTION__) */
-#define ptrace xc_ptrace
-long xc_ptrace(enum __ptrace_request request, ...);
+long (*myptrace)(enum __ptrace_request, pid_t, long, long);
+int (*myxcwait)(int domain, int *status, int options) ;
 
-int waitdomain(int domain, int *status, int options);
 
 #define DOMFLAGS_DYING     (1<<0) /* Domain is scheduled to die.             */
 #define DOMFLAGS_CRASHED   (1<<1) /* Crashed domain; frozen for postmortem.  */
@@ -61,6 +60,8 @@ static void linux_resume (struct thread_resume *resume_info);
 
 int debug_threads;
 int using_threads;
+extern int isfile;
+
 struct pending_signals
 {
   int signal;
@@ -150,7 +151,7 @@ linux_attach (int domain)
 {
     struct process_info *new_process;
     current_domain = domain;
-    if (ptrace (PTRACE_ATTACH, domain, 0, 0) != 0) {
+    if (myptrace (PTRACE_ATTACH, domain, 0, 0) != 0) {
        fprintf (stderr, "Cannot attach to domain %d: %s (%d)\n", domain,
                 strerror (errno), errno);
        fflush (stderr);
@@ -173,7 +174,7 @@ linux_kill_one_process (struct inferior_list_entry *entry)
 {
   struct thread_info *thread = (struct thread_info *) entry;
   struct process_info *process = get_thread_process (thread);
-  ptrace (PTRACE_KILL, pid_of (process), 0, 0);
+  myptrace (PTRACE_KILL, pid_of (process), 0, 0);
 
 }
 
@@ -190,7 +191,7 @@ linux_detach_one_process (struct inferior_list_entry *entry)
   struct thread_info *thread = (struct thread_info *) entry;
   struct process_info *process = get_thread_process (thread);
 
-  ptrace (PTRACE_DETACH, pid_of (process), 0, 0);
+  myptrace (PTRACE_DETACH, pid_of (process), 0, 0);
 }
 
 
@@ -216,8 +217,7 @@ static unsigned char
 linux_wait (char *status)
 {
   int w;
-  TRACE_ENTER;
-  if (waitdomain(current_domain, &w, 0))
+  if (myxcwait(current_domain, &w, 0))
       return -1;
   
   if (w & (DOMFLAGS_CRASHED|DOMFLAGS_DYING)) {
@@ -242,7 +242,7 @@ linux_resume (struct thread_resume *resume_info)
   expect_signal = resume_info->sig;
   for_each_inferior(&all_threads, regcache_invalidate_one);
 
-  ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, current_domain, 0, 0);
+  myptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, current_domain, 0, 0);
 
 }
 
@@ -266,7 +266,7 @@ regsets_fetch_inferior_registers ()
        }
 
       buf = malloc (regset->size);
-      res = ptrace (regset->get_request, inferior_pid, 0, buf);
+      res = myptrace (regset->get_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
       if (res < 0)
        {
          if (errno == EIO)
@@ -318,7 +318,7 @@ regsets_store_inferior_registers ()
 
       buf = malloc (regset->size);
       regset->fill_function (buf);
-      res = ptrace (regset->set_request, inferior_pid, 0, buf);
+      res = myptrace (regset->set_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
       if (res < 0)
        {
          if (errno == EIO)
@@ -395,7 +395,7 @@ linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len)
   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
     {
       errno = 0;
-      buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
+      buffer[i] = myptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
       if (errno)
        return errno;
     }
@@ -428,13 +428,13 @@ linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
 
   /* Fill start and end extra bytes of buffer with existing memory data.  */
 
-  buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid,
+  buffer[0] = myptrace (PTRACE_PEEKTEXT, inferior_pid,
                      (PTRACE_ARG3_TYPE) addr, 0);
 
   if (count > 1)
     {
       buffer[count - 1]
-       = ptrace (PTRACE_PEEKTEXT, inferior_pid,
+       = myptrace (PTRACE_PEEKTEXT, inferior_pid,
                  (PTRACE_ARG3_TYPE) (addr + (count - 1)
                                      * sizeof (PTRACE_XFER_TYPE)),
                  0);
@@ -448,7 +448,7 @@ linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
   for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
     {
       errno = 0;
-      ptrace (PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]);
+      myptrace (PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]);
       if (errno)
        return errno;
     }
@@ -545,4 +545,12 @@ initialize_low (void)
                       the_low_target.breakpoint_len);
   init_registers ();
   linux_init_signals ();
+  if (isfile) {
+      myptrace = xc_ptrace_core;
+      myxcwait = xc_waitdomain_core;
+  } else {
+      myptrace = xc_ptrace;
+      myxcwait = xc_waitdomain;
+  }
+
 }
diff --git a/tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c b/tools/gdb/gdb-6.2.1-xen-sparse/gdb/gdbserver/server.c
new file mode 100644 (file)
index 0000000..54f508a
--- /dev/null
@@ -0,0 +1,639 @@
+/* Main code for remote server for GDB.
+   Copyright 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "server.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int cont_thread;
+int general_thread;
+int step_thread;
+int thread_from_wait;
+int old_thread_from_wait;
+int extended_protocol;
+int server_waiting;
+int isfile = 0;
+
+jmp_buf toplevel;
+
+/* The PID of the originally created or attached inferior.  Used to
+   send signals to the process when GDB sends us an asynchronous interrupt
+   (user hitting Control-C in the client), and to wait for the child to exit
+   when no longer debugging it.  */
+
+int signal_pid;
+
+static unsigned char
+start_inferior (char *argv[], char *statusptr)
+{
+  signal (SIGTTOU, SIG_DFL);
+  signal (SIGTTIN, SIG_DFL);
+
+  signal_pid = create_inferior (argv[0], argv);
+
+  fprintf (stderr, "Process %s created; pid = %d\n", argv[0],
+          signal_pid);
+
+  signal (SIGTTOU, SIG_IGN);
+  signal (SIGTTIN, SIG_IGN);
+  tcsetpgrp (fileno (stderr), signal_pid);
+
+  /* Wait till we are at 1st instruction in program, return signal number.  */
+  return mywait (statusptr, 0);
+}
+
+static int
+attach_inferior (int pid, char *statusptr, unsigned char *sigptr)
+{
+  /* myattach should return -1 if attaching is unsupported,
+     0 if it succeeded, and call error() otherwise.  */
+
+  if (myattach (pid) != 0)
+    return -1;
+
+  fprintf (stderr, "Attached; pid = %d\n", pid);
+
+  /* FIXME - It may be that we should get the SIGNAL_PID from the
+     attach function, so that it can be the main thread instead of
+     whichever we were told to attach to.  */
+  signal_pid = pid;
+
+  *sigptr = mywait (statusptr, 0);
+
+  return 0;
+}
+
+extern int remote_debug;
+
+/* Handle all of the extended 'q' packets.  */
+void
+handle_query (char *own_buf)
+{
+  static struct inferior_list_entry *thread_ptr;
+
+  if (strcmp ("qSymbol::", own_buf) == 0)
+    {
+      if (the_target->look_up_symbols != NULL)
+       (*the_target->look_up_symbols) ();
+
+      strcpy (own_buf, "OK");
+      return;
+    }
+
+  if (strcmp ("qfThreadInfo", own_buf) == 0)
+    {
+      thread_ptr = all_threads.head;
+      sprintf (own_buf, "m%x", thread_ptr->id);
+      thread_ptr = thread_ptr->next;
+      return;
+    }
+
+  if (strcmp ("qsThreadInfo", own_buf) == 0)
+    {
+      if (thread_ptr != NULL)
+       {
+         sprintf (own_buf, "m%x", thread_ptr->id);
+         thread_ptr = thread_ptr->next;
+         return;
+       }
+      else
+       {
+         sprintf (own_buf, "l");
+         return;
+       }
+    }
+
+  if (the_target->read_auxv != NULL
+      && strncmp ("qPart:auxv:read::", own_buf, 17) == 0)
+    {
+      char data[(PBUFSIZ - 1) / 2];
+      CORE_ADDR ofs;
+      unsigned int len;
+      int n;
+      decode_m_packet (&own_buf[17], &ofs, &len); /* "OFS,LEN" */
+      if (len > sizeof data)
+       len = sizeof data;
+      n = (*the_target->read_auxv) (ofs, data, len);
+      if (n == 0)
+       write_ok (own_buf);
+      else if (n < 0)
+       write_enn (own_buf);
+      else
+       convert_int_to_ascii (data, own_buf, n);
+      return;
+    }
+
+  /* Otherwise we didn't know what packet it was.  Say we didn't
+     understand it.  */
+  own_buf[0] = 0;
+}
+
+/* Parse vCont packets.  */
+void
+handle_v_cont (char *own_buf, char *status, unsigned char *signal)
+{
+  char *p, *q;
+  int n = 0, i = 0;
+  struct thread_resume *resume_info, default_action;
+
+  /* Count the number of semicolons in the packet.  There should be one
+     for every action.  */
+  p = &own_buf[5];
+  while (p)
+    {
+      n++;
+      p++;
+      p = strchr (p, ';');
+    }
+  /* Allocate room for one extra action, for the default remain-stopped
+     behavior; if no default action is in the list, we'll need the extra
+     slot.  */
+  resume_info = malloc ((n + 1) * sizeof (resume_info[0]));
+
+  default_action.thread = -1;
+  default_action.leave_stopped = 1;
+  default_action.step = 0;
+  default_action.sig = 0;
+
+  p = &own_buf[5];
+  i = 0;
+  while (*p)
+    {
+      p++;
+
+      resume_info[i].leave_stopped = 0;
+
+      if (p[0] == 's' || p[0] == 'S')
+       resume_info[i].step = 1;
+      else if (p[0] == 'c' || p[0] == 'C')
+       resume_info[i].step = 0;
+      else
+       goto err;
+
+      if (p[0] == 'S' || p[0] == 'C')
+       {
+         int sig;
+         sig = strtol (p + 1, &q, 16);
+         if (p == q)
+           goto err;
+         p = q;
+
+         if (!target_signal_to_host_p (sig))
+           goto err;
+         resume_info[i].sig = target_signal_to_host (sig);
+       }
+      else
+       {
+         resume_info[i].sig = 0;
+         p = p + 1;
+       }
+
+      if (p[0] == 0)
+       {
+         resume_info[i].thread = -1;
+         default_action = resume_info[i];
+
+         /* Note: we don't increment i here, we'll overwrite this entry
+            the next time through.  */
+       }
+      else if (p[0] == ':')
+       {
+         resume_info[i].thread = strtol (p + 1, &q, 16);
+         if (p == q)
+           goto err;
+         p = q;
+         if (p[0] != ';' && p[0] != 0)
+           goto err;
+
+         i++;
+       }
+    }
+
+  resume_info[i] = default_action;
+
+  /* Still used in occasional places in the backend.  */
+  if (n == 1 && resume_info[0].thread != -1)
+    cont_thread = resume_info[0].thread;
+  else
+    cont_thread = -1;
+  set_desired_inferior (0);
+
+  (*the_target->resume) (resume_info);
+
+  free (resume_info);
+
+  *signal = mywait (status, 1);
+  prepare_resume_reply (own_buf, *status, *signal);
+  return;
+
+err:
+  /* No other way to report an error... */
+  strcpy (own_buf, "");
+  free (resume_info);
+  return;
+}
+
+/* Handle all of the extended 'v' packets.  */
+void
+handle_v_requests (char *own_buf, char *status, unsigned char *signal)
+{
+  if (strncmp (own_buf, "vCont;", 6) == 0)
+    {
+      handle_v_cont (own_buf, status, signal);
+      return;
+    }
+
+  if (strncmp (own_buf, "vCont?", 6) == 0)
+    {
+      strcpy (own_buf, "vCont;c;C;s;S");
+      return;
+    }
+
+  /* Otherwise we didn't know what packet it was.  Say we didn't
+     understand it.  */
+  own_buf[0] = 0;
+  return;
+}
+
+void
+myresume (int step, int sig)
+{
+  struct thread_resume resume_info[2];
+  int n = 0;
+
+  if (step || sig || cont_thread > 0)
+    {
+      resume_info[0].thread
+       = ((struct inferior_list_entry *) current_inferior)->id;
+      resume_info[0].step = step;
+      resume_info[0].sig = sig;
+      resume_info[0].leave_stopped = 0;
+      n++;
+    }
+  resume_info[n].thread = -1;
+  resume_info[n].step = 0;
+  resume_info[n].sig = 0;
+  resume_info[n].leave_stopped = (cont_thread > 0);
+
+  (*the_target->resume) (resume_info);
+}
+
+static int attached;
+
+static void
+gdbserver_usage (void)
+{
+  error ("Usage:\tgdbserver COMM PROG [ARGS ...]\n"
+        "\tgdbserver COMM --attach PID\n"
+        "\tgdbserver COMM --file COREFILE\n"
+        "\n"
+        "COMM may either be a tty device (for serial debugging), or \n"
+        "HOST:PORT to listen for a TCP connection.\n");
+}
+
+int
+main (int argc, char *argv[])
+{
+  char ch, status, *own_buf, mem_buf[2000];
+  int i = 0;
+  unsigned char signal;
+  unsigned int len;
+  CORE_ADDR mem_addr;
+  int bad_attach;
+  int pid;
+  char *arg_end;
+
+  if (setjmp (toplevel))
+    {
+      fprintf (stderr, "Exiting\n");
+      exit (1);
+    }
+
+  bad_attach = 0;
+  pid = 0;
+  attached = 0;
+  if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
+    {
+      if (argc == 4
+         && argv[3] != '\0'
+         && (pid = strtoul (argv[3], &arg_end, 10)) != 0
+         && *arg_end == '\0')
+       {
+         ;
+       }
+      else
+       bad_attach = 1;
+    }
+  else if (argc >= 3 && strcmp (argv[2], "--file") == 0)
+    {
+      if (argc == 4
+         && argv[3] != '\0')
+       {
+         if ((pid = open(argv[3], O_RDONLY)) <= 0) 
+           bad_attach = 1;
+         else 
+           isfile = 1;
+       }
+      else
+       bad_attach = 1;
+    }
+
+  if (argc < 3 || bad_attach)
+    gdbserver_usage();
+
+  initialize_low ();
+
+  own_buf = malloc (PBUFSIZ);
+
+  if (pid == 0)
+    {
+      /* Wait till we are at first instruction in program.  */
+      signal = start_inferior (&argv[2], &status);
+
+      /* We are now stopped at the first instruction of the target process */
+    }
+  else
+    {
+      switch (attach_inferior (pid, &status, &signal))
+       {
+       case -1:
+         error ("Attaching not supported on this target");
+         break;
+       default:
+         attached = 1;
+         break;
+       }
+    }
+
+  while (1)
+    {
+      remote_open (argv[1]);
+
+    restart:
+      setjmp (toplevel);
+      while (getpkt (own_buf) > 0)
+       {
+         unsigned char sig;
+         i = 0;
+         ch = own_buf[i++];
+         switch (ch)
+           {
+           case 'q':
+             handle_query (own_buf);
+             break;
+           case 'd':
+             remote_debug = !remote_debug;
+             break;
+           case 'D':
+             fprintf (stderr, "Detaching from inferior\n");
+             detach_inferior ();
+             write_ok (own_buf);
+             putpkt (own_buf);
+             remote_close ();
+
+             /* If we are attached, then we can exit.  Otherwise, we need to
+                hang around doing nothing, until the child is gone.  */
+             if (!attached)
+               {
+                 int status, ret;
+
+                 do {
+                   ret = waitpid (signal_pid, &status, 0);
+                   if (WIFEXITED (status) || WIFSIGNALED (status))
+                     break;
+                 } while (ret != -1 || errno != ECHILD);
+               }
+
+             exit (0);
+
+           case '!':
+             if (attached == 0)
+               {
+                 extended_protocol = 1;
+                 prepare_resume_reply (own_buf, status, signal);
+               }
+             else
+               {
+                 /* We can not use the extended protocol if we are
+                    attached, because we can not restart the running
+                    program.  So return unrecognized.  */
+                 own_buf[0] = '\0';
+               }
+             break;
+           case '?':
+             prepare_resume_reply (own_buf, status, signal);
+             break;
+           case 'H':
+             switch (own_buf[1])
+               {
+               case 'g':
+                 general_thread = strtol (&own_buf[2], NULL, 16);
+                 write_ok (own_buf);
+                 set_desired_inferior (1);
+                 break;
+               case 'c':
+                 cont_thread = strtol (&own_buf[2], NULL, 16);
+                 write_ok (own_buf);
+                 break;
+               case 's':
+                 step_thread = strtol (&own_buf[2], NULL, 16);
+                 write_ok (own_buf);
+                 break;
+               default:
+                 /* Silently ignore it so that gdb can extend the protocol
+                    without compatibility headaches.  */
+                 own_buf[0] = '\0';
+                 break;
+               }
+             break;
+           case 'g':
+             set_desired_inferior (1);
+             registers_to_string (own_buf);
+             break;
+           case 'G':
+             set_desired_inferior (1);
+             registers_from_string (&own_buf[1]);
+             write_ok (own_buf);
+             break;
+           case 'm':
+             decode_m_packet (&own_buf[1], &mem_addr, &len);
+             if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
+               convert_int_to_ascii (mem_buf, own_buf, len);
+             else
+               write_enn (own_buf);
+             break;
+           case 'M':
+             decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
+             if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
+               write_ok (own_buf);
+             else
+               write_enn (own_buf);
+             break;
+           case 'C':
+             convert_ascii_to_int (own_buf + 1, &sig, 1);
+             if (target_signal_to_host_p (sig))
+               signal = target_signal_to_host (sig);
+             else
+               signal = 0;
+             set_desired_inferior (0);
+             myresume (0, signal);
+             signal = mywait (&status, 1);
+             prepare_resume_reply (own_buf, status, signal);
+             break;
+           case 'S':
+             convert_ascii_to_int (own_buf + 1, &sig, 1);
+             if (target_signal_to_host_p (sig))
+               signal = target_signal_to_host (sig);
+             else
+               signal = 0;
+             set_desired_inferior (0);
+             myresume (1, signal);
+             signal = mywait (&status, 1);
+             prepare_resume_reply (own_buf, status, signal);
+             break;
+           case 'c':
+             set_desired_inferior (0);
+             myresume (0, 0);
+             signal = mywait (&status, 1);
+             prepare_resume_reply (own_buf, status, signal);
+             break;
+           case 's':
+             set_desired_inferior (0);
+             myresume (1, 0);
+             signal = mywait (&status, 1);
+             prepare_resume_reply (own_buf, status, signal);
+             break;
+           case 'k':
+             fprintf (stderr, "Killing inferior\n");
+             kill_inferior ();
+             /* When using the extended protocol, we start up a new
+                debugging session.   The traditional protocol will
+                exit instead.  */
+             if (extended_protocol)
+               {
+                 write_ok (own_buf);
+                 fprintf (stderr, "GDBserver restarting\n");
+
+                 /* Wait till we are at 1st instruction in prog.  */
+                 signal = start_inferior (&argv[2], &status);
+                 goto restart;
+                 break;
+               }
+             else
+               {
+                 exit (0);
+                 break;
+               }
+           case 'T':
+             if (mythread_alive (strtol (&own_buf[1], NULL, 16)))
+               write_ok (own_buf);
+             else
+               write_enn (own_buf);
+             break;
+           case 'R':
+             /* Restarting the inferior is only supported in the
+                extended protocol.  */
+             if (extended_protocol)
+               {
+                 kill_inferior ();
+                 write_ok (own_buf);
+                 fprintf (stderr, "GDBserver restarting\n");
+
+                 /* Wait till we are at 1st instruction in prog.  */
+                 signal = start_inferior (&argv[2], &status);
+                 goto restart;
+                 break;
+               }
+             else
+               {
+                 /* It is a request we don't understand.  Respond with an
+                    empty packet so that gdb knows that we don't support this
+                    request.  */
+                 own_buf[0] = '\0';
+                 break;
+               }
+           case 'v':
+             /* Extended (long) request.  */
+             handle_v_requests (own_buf, &status, &signal);
+             break;
+           default:
+             /* It is a request we don't understand.  Respond with an
+                empty packet so that gdb knows that we don't support this
+                request.  */
+             own_buf[0] = '\0';
+             break;
+           }
+
+         putpkt (own_buf);
+
+         if (status == 'W')
+           fprintf (stderr,
+                    "\nChild exited with status %d\n", signal);
+         if (status == 'X')
+           fprintf (stderr, "\nChild terminated with signal = 0x%x\n",
+                    signal);
+         if (status == 'W' || status == 'X')
+           {
+             if (extended_protocol)
+               {
+                 fprintf (stderr, "Killing inferior\n");
+                 kill_inferior ();
+                 write_ok (own_buf);
+                 fprintf (stderr, "GDBserver restarting\n");
+
+                 /* Wait till we are at 1st instruction in prog.  */
+                 signal = start_inferior (&argv[2], &status);
+                 goto restart;
+                 break;
+               }
+             else
+               {
+                 fprintf (stderr, "GDBserver exiting\n");
+                 exit (0);
+               }
+           }
+       }
+
+      /* We come here when getpkt fails.
+
+         For the extended remote protocol we exit (and this is the only
+         way we gracefully exit!).
+
+         For the traditional remote protocol close the connection,
+         and re-open it at the top of the loop.  */
+      if (extended_protocol)
+       {
+         remote_close ();
+         exit (0);
+       }
+      else
+       {
+         fprintf (stderr, "Remote side has terminated connection.  "
+                          "GDBserver will reopen the connection.\n");
+         remote_close ();
+       }
+    }
+}
index eea521875431cf321d820cf61d3c2923ea354b1c..e458444bb053ca2b0e6d8d79504f5f7a3da89c63 100644 (file)
@@ -17,6 +17,7 @@ INCLUDES += -I $(XEN_LIBXUTIL)
 
 SRCS     :=
 SRCS     += xc_bvtsched.c
+SRCS     += xc_core.c
 SRCS     += xc_domain.c
 SRCS     += xc_evtchn.c
 SRCS     += xc_gnttab.c
@@ -29,6 +30,7 @@ SRCS     += xc_misc.c
 SRCS     += xc_physdev.c
 SRCS     += xc_private.c
 SRCS     += xc_ptrace.c
+SRCS     += xc_ptrace_core.c
 SRCS     += xc_vmx_build.c
 
 CFLAGS   += -Wall
index 9eac0a7a180c78bcd1868778d545779101c0bf7b..80a3914208eae97ade56b72a7b36c95063aae6e9 100644 (file)
@@ -20,6 +20,7 @@ typedef int16_t            s16;
 typedef int32_t            s32;
 typedef int64_t            s64;
 
+#include <sys/ptrace.h>
 #include <xen/xen.h>
 #include <xen/dom0_ops.h>
 #include <xen/event_channel.h>
@@ -71,6 +72,38 @@ int xc_interface_open(void);
  */
 int xc_interface_close(int xc_handle);
 
+/*
+ * DOMAIN DEBUGGING FUNCTIONS
+ */
+
+typedef struct xc_core_header {
+    unsigned int xch_magic;
+    unsigned int xch_nr_vcpus;
+    unsigned int xch_nr_pages;
+    unsigned int xch_ctxt_offset;
+    unsigned int xch_index_offset;
+    unsigned int xch_pages_offset;
+} xc_core_header_t;
+
+
+long xc_ptrace(enum __ptrace_request request, 
+              pid_t pid, 
+              long addr, 
+              long data);
+
+long xc_ptrace_core(enum __ptrace_request request, 
+                   pid_t pid, 
+                   long addr, 
+                   long data);
+
+int xc_waitdomain(int domain, 
+                 int *status, 
+                 int options);
+
+int xc_waitdomain_core(int domain, 
+                      int *status, 
+                      int options);
+
 /*
  * DOMAIN MANAGEMENT FUNCTIONS
  */
@@ -94,6 +127,12 @@ int xc_domain_create(int xc_handle,
                      float cpu_weight,
                      u32 *pdomid);
 
+
+int xc_domain_dumpcore(int xc_handle, 
+                      u32 domid,
+                      const char *corename);
+
+
 /**
  * This function pauses a domain. A paused domain still exists in memory
  * however it does not receive any timeslices from the hypervisor.
diff --git a/tools/libxc/xc_core.c b/tools/libxc/xc_core.c
new file mode 100644 (file)
index 0000000..84ef16b
--- /dev/null
@@ -0,0 +1,99 @@
+#include "xc_private.h"
+#define ELFSIZE 32
+#include "xc_elf.h"
+#include <stdlib.h>
+#include <zlib.h>
+
+/* number of pages to write at a time */
+#define DUMP_INCREMENT 4 * 1024
+#define round_pgup(_p)    (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
+static int
+copy_from_domain_page(int xc_handle,
+                     u32 domid,
+                     unsigned long *page_array,
+                     unsigned long src_pfn,
+                     void *dst_page)
+{
+    void *vaddr = xc_map_foreign_range(
+        xc_handle, domid, PAGE_SIZE, PROT_READ, page_array[src_pfn]);
+    if ( vaddr == NULL )
+        return -1;
+    memcpy(dst_page, vaddr, PAGE_SIZE);
+    munmap(vaddr, PAGE_SIZE);
+    return 0;
+}
+
+int 
+xc_domain_dumpcore(int xc_handle,
+                  u32 domid,
+                  const char *corename)
+{
+       vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt;
+       unsigned long nr_pages;
+       unsigned long *page_array;
+       xc_domaininfo_t st_info, *info = &st_info;
+       int i, dump_fd;
+       char *dump_mem, *dump_mem_start = NULL;
+       struct xc_core_header header;
+       
+       if ((dump_fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0) {
+               PERROR("Could not open corefile %s: %s", corename, strerror(errno));
+               goto error_out;
+       }
+       
+       if ((dump_mem_start = malloc(DUMP_INCREMENT*PAGE_SIZE)) == 0) {
+               PERROR("Could not allocate dump_mem");
+               goto error_out;
+       }
+       
+       if (xc_domain_getfullinfo(xc_handle, domid, 0/* XXX hardcode */, info, ctxt)) {
+               PERROR("Could not get full info for domain");
+               goto error_out;
+       }
+
+       nr_pages = info->tot_pages;
+       header.xch_magic = 0xF00FEBED; 
+       header.xch_nr_vcpus = 1; /* no interface to query at the moment */
+       header.xch_nr_pages = nr_pages;
+       header.xch_ctxt_offset = sizeof(struct xc_core_header);
+       header.xch_index_offset = sizeof(struct xc_core_header) +
+           sizeof(vcpu_guest_context_t);
+       header.xch_pages_offset = round_pgup(sizeof(struct xc_core_header) +
+           sizeof(vcpu_guest_context_t) + nr_pages * sizeof(unsigned long));
+
+       write(dump_fd, &header, sizeof(struct xc_core_header));
+       write(dump_fd, ctxt, sizeof(st_ctxt));
+
+       if ((page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL) {
+           printf("Could not allocate memory\n");
+           goto error_out;
+       }
+       if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) {
+           printf("Could not get the page frame list\n");
+           goto error_out;
+       }
+       write(dump_fd, page_array, nr_pages * sizeof(unsigned long));
+       lseek(dump_fd, header.xch_pages_offset, SEEK_SET);
+       for (dump_mem = dump_mem_start, i = 0; i < nr_pages; i++) {
+               copy_from_domain_page(xc_handle, domid, page_array, i, dump_mem);
+               dump_mem += PAGE_SIZE;
+               if (((i + 1) % DUMP_INCREMENT == 0) || (i + 1) == nr_pages) {
+                       if (write(dump_fd, dump_mem_start, dump_mem - dump_mem_start) < 
+                           dump_mem - dump_mem_start) {
+                               PERROR("Partial write, file system full?");
+                               goto error_out;
+                       }
+                       dump_mem = dump_mem_start;
+               }
+       }
+
+       close(dump_fd);
+       free(dump_mem_start);
+       return 0;
+ error_out:
+       if (dump_fd)
+               close(dump_fd);
+       if (dump_mem_start)
+               free(dump_mem_start);
+       return -1;
+}
index df05b59d4e4ab31550143d12c5e4d58569834126..287680573fd0ed8ec2a88eb1b8f2299be4854c77 100644 (file)
@@ -17,8 +17,7 @@
  * ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
  */
 
-long xc_ptrace(enum __ptrace_request request, 
-              pid_t pid, void *addr, void *data);
+
 int waitdomain(int domain, int *status, int options);
 
 char * ptrace_names[] = {
@@ -218,7 +217,7 @@ map_domain_va(unsigned long domid, int cpu, void * guest_va, int perm)
 }
 
 int 
-waitdomain(int domain, int *status, int options)
+xc_waitdomain(int domain, int *status, int options)
 {
     dom0_op_t op;
     int retval;
@@ -260,7 +259,7 @@ waitdomain(int domain, int *status, int options)
 }
 
 long
-xc_ptrace(enum __ptrace_request request, pid_t domid, void *addr, void *data)
+xc_ptrace(enum __ptrace_request request, pid_t domid, long eaddr, long edata)
 {
     dom0_op_t       op;
     int             status = 0;
@@ -268,6 +267,8 @@ xc_ptrace(enum __ptrace_request request, pid_t domid, void *addr, void *data)
     long            retval = 0;
     unsigned long  *guest_va;
     int             cpu = VCPU;
+    void           *addr = (char *)eaddr;
+    void           *data = (char *)edata;
 
     op.interface_version = DOM0_INTERFACE_VERSION;
     
diff --git a/tools/libxc/xc_ptrace_core.c b/tools/libxc/xc_ptrace_core.c
new file mode 100644 (file)
index 0000000..87b88ed
--- /dev/null
@@ -0,0 +1,287 @@
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include "xc_private.h"
+#include <asm/elf.h>
+#include <time.h>
+
+
+#define BSD_PAGE_MASK  (PAGE_SIZE-1)
+#define        PG_FRAME        (~((unsigned long)BSD_PAGE_MASK)
+#define PDRSHIFT        22
+#define        PSL_T           0x00000100      /* trace enable bit */
+
+#define VCPU            0               /* XXX */
+
+/*
+ * long  
+ * ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
+ */
+
+
+struct gdb_regs {
+    long ebx; /* 0 */
+    long ecx; /* 4 */
+    long edx; /* 8 */
+    long esi; /* 12 */
+    long edi; /* 16 */
+    long ebp; /* 20 */
+    long eax; /* 24 */ 
+    int  xds; /* 28 */
+    int  xes; /* 32 */
+    int  xfs; /* 36 */
+    int  xgs; /* 40 */
+    long orig_eax; /* 44 */
+    long eip;    /* 48 */
+    int  xcs;    /* 52 */
+    long eflags; /* 56 */
+    long esp;    /* 60 */     
+    int  xss;    /* 64 */
+};
+
+#define printval(x) printf("%s = %lx\n", #x, (long)x);
+#define SET_PT_REGS(pt, xc) \
+{ \
+    pt.ebx = xc.ebx; \
+    pt.ecx = xc.ecx; \
+    pt.edx = xc.edx; \
+    pt.esi = xc.esi; \
+    pt.edi = xc.edi; \
+    pt.ebp = xc.ebp; \
+    pt.eax = xc.eax; \
+    pt.eip = xc.eip; \
+    pt.xcs = xc.cs; \
+    pt.eflags = xc.eflags; \
+    pt.esp = xc.esp; \
+    pt.xss = xc.ss; \
+    pt.xes = xc.es; \
+    pt.xds = xc.ds; \
+    pt.xfs = xc.fs; \
+    pt.xgs = xc.gs; \
+}
+
+#define SET_XC_REGS(pt, xc) \
+{ \
+    xc.ebx = pt->ebx; \
+    xc.ecx = pt->ecx; \
+    xc.edx = pt->edx; \
+    xc.esi = pt->esi; \
+    xc.edi = pt->edi; \
+    xc.ebp = pt->ebp; \
+    xc.eax = pt->eax; \
+    xc.eip = pt->eip; \
+    xc.cs = pt->xcs; \
+    xc.eflags = pt->eflags; \
+    xc.esp = pt->esp; \
+    xc.ss = pt->xss; \
+    xc.es = pt->xes; \
+    xc.ds = pt->xds; \
+    xc.fs = pt->xfs; \
+    xc.gs = pt->xgs; \
+}
+
+
+#define vtopdi(va) ((va) >> PDRSHIFT)
+#define vtopti(va) (((va) >> PAGE_SHIFT) & 0x3ff)
+
+/* XXX application state */
+
+
+static long                    nr_pages = 0;
+static unsigned long           *p2m_array = NULL;
+static unsigned long           *m2p_array = NULL;
+static unsigned long            pages_offset;
+static unsigned long            cr3[MAX_VIRT_CPUS];
+static vcpu_guest_context_t     ctxt[MAX_VIRT_CPUS];
+
+/* --------------------- */
+
+static unsigned long
+map_mtop_offset(unsigned long ma)
+{
+    return pages_offset + (m2p_array[ma >> PAGE_SHIFT] << PAGE_SHIFT);
+}
+
+
+static void *
+map_domain_va(unsigned long domfd, int cpu, void * guest_va)
+{
+    unsigned long pde, page;
+    unsigned long va = (unsigned long)guest_va;
+
+    static unsigned long  cr3_phys[MAX_VIRT_CPUS];
+    static unsigned long *cr3_virt[MAX_VIRT_CPUS];
+    static unsigned long  pde_phys[MAX_VIRT_CPUS];
+    static unsigned long *pde_virt[MAX_VIRT_CPUS];
+    static unsigned long  page_phys[MAX_VIRT_CPUS];
+    static unsigned long *page_virt[MAX_VIRT_CPUS];
+
+    if (cr3[cpu] != cr3_phys[cpu]) 
+    {
+       cr3_phys[cpu] = cr3[cpu];
+       if (cr3_virt[cpu])
+           munmap(cr3_virt[cpu], PAGE_SIZE);
+       if ((cr3_virt[cpu] = mmap(NULL, PAGE_SIZE, PROT_READ, 
+                                 MAP_PRIVATE, domfd, map_mtop_offset(cr3_phys[cpu]))) == 
+           (unsigned long*)0xffffffff)
+       {
+           perror("mmap failed");
+           goto error_out;
+       }
+    } 
+    if ((pde = cr3_virt[cpu][vtopdi(va)]) == 0) /* logical address */
+       goto error_out;
+    if (ctxt[cpu].flags & VGCF_VMX_GUEST)
+       pde = p2m_array[pde >> PAGE_SHIFT] << PAGE_SHIFT;
+    if (pde != pde_phys[cpu]) 
+    {
+       pde_phys[cpu] = pde;
+       if (pde_virt[cpu])
+           munmap(pde_virt[cpu], PAGE_SIZE);
+       if ((pde_virt[cpu] =  mmap(NULL, PAGE_SIZE, PROT_READ, 
+                                 MAP_PRIVATE, domfd, map_mtop_offset(pde_phys[cpu]))) == NULL)
+           goto error_out;
+    }
+    if ((page = pde_virt[cpu][vtopti(va)]) == 0) /* logical address */
+       goto error_out;
+    if (ctxt[cpu].flags & VGCF_VMX_GUEST)
+       page = p2m_array[page >> PAGE_SHIFT] << PAGE_SHIFT;
+    if (page != page_phys[cpu]) 
+    {
+       page_phys[cpu] = page;
+       if (page_virt[cpu])
+           munmap(page_virt[cpu], PAGE_SIZE);
+       if ((page_virt[cpu] = mmap(NULL, PAGE_SIZE, PROT_READ, 
+                                 MAP_PRIVATE, domfd, map_mtop_offset(page_phys[cpu]))) == NULL) {
+           printf("cr3 %lx pde %lx page %lx pti %lx\n", cr3[cpu], pde, page, vtopti(va));
+           page_phys[cpu] = 0;
+           goto error_out;
+       }
+    }  
+    return (void *)(((unsigned long)page_virt[cpu]) | (va & BSD_PAGE_MASK));
+
+ error_out:
+    return 0;
+}
+
+int 
+xc_waitdomain_core(int domfd, int *status, int options)
+{
+    int retval = -1;
+    int nr_vcpus;
+    int i;
+    xc_core_header_t header;
+
+    if (nr_pages == 0) {
+
+       if (read(domfd, &header, sizeof(header)) != sizeof(header))
+           return -1;
+
+       nr_pages = header.xch_nr_pages;
+       nr_vcpus = header.xch_nr_vcpus;
+       pages_offset = header.xch_pages_offset;
+
+       if (read(domfd, ctxt, sizeof(vcpu_guest_context_t)*nr_vcpus) != 
+           sizeof(vcpu_guest_context_t)*nr_vcpus)
+           return -1;
+
+       for (i = 0; i < nr_vcpus; i++) {
+           cr3[i] = ctxt[i].pt_base;
+       }
+       if ((p2m_array = malloc(nr_pages * sizeof(unsigned long))) == NULL) {
+           printf("Could not allocate p2m_array\n");
+           goto error_out;
+       }
+       if (read(domfd, p2m_array, sizeof(unsigned long)*nr_pages) != 
+           sizeof(unsigned long)*nr_pages)
+           return -1;
+
+       if ((m2p_array = malloc((1<<20) * sizeof(unsigned long))) == NULL) {
+           printf("Could not allocate m2p array\n");
+           goto error_out;
+       }
+       bzero(m2p_array, sizeof(unsigned long)* 1 << 20);
+
+       for (i = 0; i < nr_pages; i++) {
+           m2p_array[p2m_array[i]] = i;
+       }
+
+    }
+    retval = 0;
+ error_out:
+    return retval;
+
+}
+
+long
+xc_ptrace_core(enum __ptrace_request request, int domfd, long eaddr, long edata)
+{
+    int             status = 0;
+    struct gdb_regs pt;
+    long            retval = 0;
+    unsigned long  *guest_va;
+    int             cpu = VCPU;
+    void           *addr = (char *)eaddr;
+    void           *data = (char *)edata;
+
+#if 0
+    printf("%20s %d, %p, %p \n", ptrace_names[request], domid, addr, data);
+#endif
+    switch (request) { 
+    case PTRACE_PEEKTEXT:
+    case PTRACE_PEEKDATA:
+       if ((guest_va = (unsigned long *)map_domain_va(domfd, cpu, addr)) == NULL) {
+           status = EFAULT;
+           goto error_out;
+       }
+
+       retval = *guest_va;
+       break;
+    case PTRACE_POKETEXT:
+    case PTRACE_POKEDATA:
+       if ((guest_va = (unsigned long *)map_domain_va(domfd, cpu, addr)) == NULL) {
+           status = EFAULT;
+           goto error_out;
+       }
+       *guest_va = (unsigned long)data;
+       break;
+    case PTRACE_GETREGS:
+    case PTRACE_GETFPREGS:
+    case PTRACE_GETFPXREGS:
+       if (request == PTRACE_GETREGS) {
+               SET_PT_REGS(pt, ctxt[cpu].user_regs); 
+               memcpy(data, &pt, sizeof(elf_gregset_t));
+       } else if (request == PTRACE_GETFPREGS)
+           memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof(ctxt[cpu].fpu_ctxt));
+       else /*if (request == PTRACE_GETFPXREGS)*/
+           memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof(ctxt[cpu].fpu_ctxt));
+       break;
+    case PTRACE_ATTACH:
+       retval = 0;
+       break;
+    case PTRACE_SETREGS:
+    case PTRACE_SINGLESTEP:
+    case PTRACE_CONT:
+    case PTRACE_DETACH:
+    case PTRACE_SETFPREGS:
+    case PTRACE_SETFPXREGS:
+    case PTRACE_PEEKUSER:
+    case PTRACE_POKEUSER:
+    case PTRACE_SYSCALL:
+    case PTRACE_KILL:
+#ifdef DEBUG
+       printf("unsupported xc_ptrace request %s\n", ptrace_names[request]);
+#endif
+       status = ENOSYS;
+       break;
+    case PTRACE_TRACEME:
+       printf("PTRACE_TRACEME is an invalid request under Xen\n");
+       status = EINVAL;
+    }
+    
+    if (status) {
+       errno = status;
+       retval = -1;
+    }
+ error_out:
+    return retval;
+}
index a474c4a888ee7312b35bb576b8174cfc590b2cba..5b76a20f43dc817d6c7c0270be9a57de73f234ba 100644 (file)
@@ -36,6 +36,33 @@ typedef struct {
  * Definitions for the 'xc' object type.
  */
 
+static PyObject *pyxc_domain_dumpcore(PyObject *self,
+                                     PyObject *args,
+                                     PyObject *kwds)
+{
+    XcObject *xc = (XcObject *)self;
+
+    u32 dom;
+    char *corefile;
+
+    static char *kwd_list[] = { "dom", "corefile", NULL };
+
+    if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list, &dom, &corefile) )
+        goto exit;
+
+    if ( (corefile == NULL) || (corefile[0] == '\0') )
+        goto exit;
+
+    if ( xc_domain_dumpcore(xc->xc_handle, dom, corefile) != 0 )
+        return PyErr_SetFromErrno(xc_error);
+    
+    Py_INCREF(zero);
+    return zero;
+
+ exit:
+    return NULL;
+}
+
 static PyObject *pyxc_domain_create(PyObject *self,
                                     PyObject *args,
                                     PyObject *kwds)
@@ -854,6 +881,14 @@ static PyMethodDef pyxc_methods[] = {
       " mem_kb [int, 0]:        Memory allocation, in kilobytes.\n"
       "Returns: [int] new domain identifier; -1 on error.\n" },
 
+    { "domain_dumpcore", 
+      (PyCFunction)pyxc_domain_dumpcore, 
+      METH_VARARGS | METH_KEYWORDS, "\n"
+      "dump core of a domain.\n"
+      " dom [int]: Identifier of domain to be paused.\n\n"
+      " corefile [string]: Name of corefile to be created.\n\n"
+      "Returns: [int] 0 on success; -1 on error.\n" },
+
     { "domain_pause", 
       (PyCFunction)pyxc_domain_pause, 
       METH_VARARGS | METH_KEYWORDS, "\n"